﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace Crystal.AI
{
    public enum EntityPathRepeatMode
    {
        IterateOnce = 0,
        IterateForever = 1,
        AutoReverse = 2,
    }

    public class WaypointPath : List<Waypoint3D>
    {         
        public Waypoint3D FirstNode { get { return this.First(); } }
        public Waypoint3D LastNode { get { return this.Last(); } }

        public Waypoint3D CurrentNode { get { return this[CurrentNodeIndex]; } }
        public Waypoint3D? NextNode
        { 
            get
            {
                try
                {
                    return this[CurrentNodeIndex + 1];
                }
                catch (IndexOutOfRangeException)
                {
                    return null;
                }
                catch (ArgumentOutOfRangeException)
                {
                    return null;
                }
            }
        }

        public PathableEntity Actor { get; private set; }
        public EntityPathRepeatMode RepeatMode { get; set; }        

        private int _CurrentNodeIndex = 0;
        protected int CurrentNodeIndex
        { 
            get { return _CurrentNodeIndex; }
            set { _CurrentNodeIndex = value; }
        }
        
        public event EventHandler NodeLanding;
        public event EventHandler PathFinished;

        //cTor
        public WaypointPath(PathableEntity actor)
            : base()
        {
            Actor = actor;
            RepeatMode = EntityPathRepeatMode.IterateOnce;
            this.PathFinished += new EventHandler(WaypointPathFinished);
        }        

        public WaypointPath(PathableEntity actor, params Waypoint3D[] waypoints)
            : base(waypoints)
        {
            Actor = actor;
            RepeatMode = EntityPathRepeatMode.IterateOnce;
            this.PathFinished += new EventHandler(WaypointPathFinished);
        }

        //Methods
        private void StepToNextNode()
        {
            _CurrentNodeIndex++;
        }

        public void WaypointPathFinished(object sender, EventArgs e)
        {
            switch (RepeatMode)
            {
                case EntityPathRepeatMode.IterateOnce: Stop(); break;
                case EntityPathRepeatMode.IterateForever:
                    {
                        Stop();
                        CurrentNodeIndex = 0;
                        Start();
                        break;
                    }
                case EntityPathRepeatMode.AutoReverse:
                    {
                        Stop();
                        this.Reverse();
                        CurrentNodeIndex = 0;
                        Start();
                        break;
                    }
            }
        }

        public virtual void Start()
        {
            Actor.IsControlled = true;
            //If the actor is not at the start position, move him (no path)
            if (!Actor.Position.Equals(FirstNode))
                Actor.Position = FirstNode;
        }
        public virtual void Stop()
        {
            Actor.IsControlled = false;      
        }
        
        public virtual void UpdatePath(GameTime time)
        {
            if (!Actor.IsControlled)
                return;

            //The path has finished
            if (NextNode == null) {
                Actor.IsControlled = false;
                PathFinished(this, null);
                return;
            }
            
            //Get the Direction to the next node
            Vector3 DirectionToNextNode = Waypoint3D.GetDistanceBetween(Actor.Position, (Waypoint3D)NextNode);

            //If there is no distance
            if (DirectionToNextNode.Equals(Vector2.Zero)) {
                //we landed
                if(NodeLanding != null)
                    NodeLanding(this, null);

                StepToNextNode();
            }
            else
            {
                //Clamp movement to speed and move;
                Vector3 moveDir = DirectionToNextNode;
                moveDir.Normalize();
                moveDir *= Actor.ActorSpeed;

                //Rotate towards (TODO: NOT SURE ABOUT THIS)
                //Actor.Rotation = CurrentNode.AngleTo((Waypoint3D)NextNode);

                //CLAMPING
                moveDir.X = (float)Math.Round(moveDir.X, 2);
                moveDir.Y = (float)Math.Round(moveDir.Y, 2);
                moveDir.Z = (float)Math.Round(moveDir.Z, 2);
                Actor.Position += moveDir;
            }
        }        
    }
}